﻿using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
using System.Threading;

namespace WindowsFormsApp1.Tools
{
    public enum LogLevel
    {
        Debug,
        Info,
        Error
    }

    /// <summary>
    /// 线程安全的日志记录器，适用于WinForm和Web应用
    /// </summary>
    public class ConcurrentLogger : IDisposable
    {
        #region 静态成员和初始化

        private static readonly ConcurrentQueue<LogEntry> _logQueue = new ConcurrentQueue<LogEntry>();
        private static readonly StringBuilder _buffer = new StringBuilder();
        private static readonly object _lock = new object();
        private static ManualResetEvent _shutdownEvent;
        private static Thread _logWriterThread;
        private static volatile bool _isRunning;
        private static string _logDirectory;
        private static int _threadRetryCount;
        private static bool _isDisposed;

        // 配置参数
        private const int BATCH_SIZE = 100;
        private const int FLUSH_INTERVAL_MS = 5000;
        private const int MAX_THREAD_RETRIES = 3;
        private const int SHUTDOWN_TIMEOUT_MS = 3000;

        static ConcurrentLogger()
        {
            Initialize();
        }

        public static void Initialize(string customLogDirectory = null)
        {
            lock (_lock)
            {
                if (_isRunning) return;

                // 设置日志目录
                _logDirectory = customLogDirectory ?? GetDefaultLogDirectory();

                // 确保日志目录存在
                if (!Directory.Exists(_logDirectory))
                {
                    Directory.CreateDirectory(_logDirectory);
                }

                _isRunning = true;
                _shutdownEvent = new ManualResetEvent(false);
                _threadRetryCount = 0;
                StartLogThread();
            }
        }

        private static string GetDefaultLogDirectory()
        {
            try
            {
                // 尝试获取网站路径
                return System.Web.HttpRuntime.AppDomainAppPath ??
                       Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            }
            catch
            {
                return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            }
        }

        private static void StartLogThread()
        {
            _logWriterThread = new Thread(LogWritingLoop)
            {
                IsBackground = true,
                Name = "LogWriterThread",
                Priority = ThreadPriority.BelowNormal
            };
            _logWriterThread.Start();
        }

        #endregion

        #region 日志记录方法

        public static void Log(LogLevel level, string message, string eventName = "Event", string methodName = null)
        {
            if (!_isRunning)
            {
                Initialize();
            }

            _logQueue.Enqueue(new LogEntry(
                level,
                message,
                eventName ?? "Event",
                methodName ?? GetCallingMethodName()
            ));
        }

        private static string GetCallingMethodName()
        {
            try
            {
                var frame = new StackTrace(2, false).GetFrame(0);
                var method = frame.GetMethod();
                return $"{method.DeclaringType?.Name}.{method.Name}";
            }
            catch
            {
                return "Unknown";
            }
        }

        #endregion

        #region 日志写入线程

        private static void LogWritingLoop()
        {
            try
            {
                while (_isRunning || !_logQueue.IsEmpty)
                {
                    // 混合等待策略：既定时检查也响应关闭信号
                    if (_shutdownEvent.WaitOne(FLUSH_INTERVAL_MS) && !_isRunning)
                        break;

                    FlushLogs();
                }
            }
            catch (ThreadAbortException)
            {
                // 线程被强制终止时的清理
                FlushLogs(force: true);
            }
            catch (Exception ex)
            {
                Trace.TraceError($"日志线程异常: {ex}");

                // 自动重启线程（最多重试3次）
                if (Interlocked.Increment(ref _threadRetryCount) <= MAX_THREAD_RETRIES && _isRunning)
                {
                    lock (_lock)
                    {
                        StartLogThread();
                    }
                }
            }
            finally
            {
                // 最终确保所有日志刷新
                FlushLogs(force: true);
            }
        }

        private static void FlushLogs(bool force = false)
        {
            var entries = new LogEntry[BATCH_SIZE];
            int count = 0;

            // 批量取出日志条目
            while ((force || count < BATCH_SIZE) && _logQueue.TryDequeue(out var entry))
            {
                entries[count++] = entry;
            }

            if (count == 0) return;

            lock (_lock)
            {
                try
                {
                    // 构建日志内容
                    for (int i = 0; i < count; i++)
                    {
                        var entry = entries[i];
                        _buffer.AppendLine(
                            $"{entry.Timestamp:yyyy-MM-dd HH:mm:ss.fff} " +
                            $"[{entry.Level}] " +
                            $"{entry.EventName} " +
                            $"{entry.MethodName} - " +
                            $"{entry.Message}"
                        );
                    }

                    // 写入文件
                    if (_buffer.Length > 0)
                    {
                        string logFileName = $"application_{DateTime.Now:yyyyMMdd}.log";
                        string logFilePath = Path.Combine(_logDirectory, logFileName);

                        // 使用FileStream确保多进程安全写入
                        using (var stream = new FileStream(
                            logFilePath,
                            FileMode.Append,
                            FileAccess.Write,
                            FileShare.ReadWrite))
                        using (var writer = new StreamWriter(stream, Encoding.UTF8))
                        {
                            writer.Write(_buffer.ToString());
                        }

                        _buffer.Clear();
                    }
                }
                catch (Exception ex)
                {
                    Trace.TraceError($"日志写入失败: {ex}");

                    // 将失败的日志重新加入队列
                    for (int i = 0; i < count; i++)
                    {
                        _logQueue.Enqueue(entries[i]);
                    }
                }
            }
        }

        #endregion

        #region 停止和清理

        public static void Stop()
        {
            lock (_lock)
            {
                if (!_isRunning) return;

                _isRunning = false;
                _shutdownEvent?.Set();

                try
                {
                    // 等待线程正常退出
                    if (_logWriterThread != null && _logWriterThread.IsAlive)
                    {
                        if (!_logWriterThread.Join(SHUTDOWN_TIMEOUT_MS))
                        {
                            // 温和地中断线程
                            _logWriterThread.Interrupt();
                        }
                    }
                }
                finally
                {
                    _shutdownEvent?.Dispose();
                    _shutdownEvent = null;
                    _logWriterThread = null;
                }
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (_isDisposed) return;

            if (disposing)
            {
                Stop();
            }

            _isDisposed = true;
        }

        ~ConcurrentLogger()
        {
            Dispose(false);
        }

        #endregion

        #region 日志条目类

        private class LogEntry
        {
            public LogLevel Level { get; }
            public DateTime Timestamp { get; }
            public string Message { get; }
            public string EventName { get; }
            public string MethodName { get; }

            public LogEntry(LogLevel level, string message, string eventName, string methodName)
            {
                Level = level;
                Timestamp = DateTime.Now;
                Message = message;
                EventName = eventName;
                MethodName = methodName;
            }
        }

        #endregion
    }
}